#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

// Modbus RTU CRC16 校验计算函数
uint16_t crc16(const uint8_t *data, int len) {
  uint16_t crc = 0xFFFF;
  for (int i = 0; i < len; i++) {
    crc ^= (uint16_t)data[i];
    for (int j = 0; j < 8; j++) {
      if (crc & 0x0001) {
        crc = (crc >> 1) ^ 0xA001;
      } else {
        crc = crc >> 1;
      }
    }
  }
  return crc;
}

// 配置串口参数
int configure_serial(int fd, int baudrate) {
  struct termios options;

  // 获取当前配置
  if (tcgetattr(fd, &options) != 0) {
    perror("tcgetattr");
    return -1;
  }

  // 设置波特率
  speed_t speed;
  switch (baudrate) {
  case 9600:
    speed = B9600;
    break;
  case 19200:
    speed = B19200;
    break;
  case 38400:
    speed = B38400;
    break;
  case 57600:
    speed = B57600;
    break;
  case 115200:
    speed = B115200;
    break;
  case 921600:
    speed = B921600;
    break;
  default:
    speed = B9600;
    break;
  }

  cfsetispeed(&options, speed);
  cfsetospeed(&options, speed);

  // 8位数据位，无校验，1位停止位
  options.c_cflag &= ~PARENB;        // 无校验
  options.c_cflag &= ~CSTOPB;        // 1位停止位
  options.c_cflag &= ~CSIZE;         // 清除数据位设置
  options.c_cflag |= CS8;            // 8位数据位
  options.c_cflag |= CREAD | CLOCAL; // 启用接收，忽略调制解調器状态线

  // 原始模式
  options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
  options.c_iflag &= ~(IXON | IXOFF | IXANY);
  options.c_iflag &= ~(INLCR | IGNCR | ICRNL);
  options.c_oflag &= ~OPOST;

  // 设置超时 (VTIME 单位是 0.1 秒) - 与您的代码保持一致
  options.c_cc[VTIME] = 10; // 1秒超时
  options.c_cc[VMIN] = 0;

  // 应用配置
  if (tcsetattr(fd, TCSANOW, &options) != 0) {
    perror("tcsetattr");
    return -1;
  }

  return 0;
}

// 新增功能: 扫描总线上的设备
void scan_bus(int fd) {
  printf("\n========== Scanning RS485 Bus (Address 1 to 247) ==========\n");
  uint8_t command[8];
  uint8_t response[64];
  int found_count = 0;

  for (int addr = 1; addr <= 247; addr++) {
    command[0] = addr;
    command[1] = 0x03;
    command[2] = 0x00;
    command[3] = 0x01;
    command[4] = 0x00;
    command[5] = 0x02;
    uint16_t crc = crc16(command, 6);
    command[6] = crc & 0xFF;
    command[7] = (crc >> 8) & 0xFF;

    tcflush(fd, TCIFLUSH);
    write(fd, command, 8);
    usleep(50000); // 等待响应

    int bytes_read = read(fd, response, sizeof(response));

    if (bytes_read > 0) {
      // 确保响应足够长以包含CRC
      if (bytes_read >= 4) {
        uint16_t received_crc = ((uint16_t)response[bytes_read - 1] << 8) |
                                response[bytes_read - 2];
        uint16_t calculated_crc = crc16(response, bytes_read - 2);
        // 检查地址和CRC是否都匹配
        if (response[0] == addr && received_crc == calculated_crc) {
          printf("--> Found device at address: 0x%02X (%d)\n", addr, addr);
          found_count++;
        }
      }
    }
    // 打印进度
    if (addr % 16 == 0) {
      fprintf(stderr, ".");
    }
  }
  fprintf(stderr, "\n");
  printf("Scan complete. Found %d device(s).\n", found_count);
  printf("=========================================================\n\n");
}

int main(int argc, char *argv[]) {
  int fd;
  const char *device = "/dev/ttyS2"; // 默认设备
  int baudrate = 9600;               // 默认波特率
  int scan_mode = 0;

  // --- 解析命令行参数 ---
  if (argc > 1 && strcmp(argv[1], "scan") == 0) {
    scan_mode = 1;
    if (argc > 2)
      device = argv[2];
    if (argc > 3)
      baudrate = atoi(argv[3]);
  } else {
    if (argc > 1)
      device = argv[1];
    if (argc > 2)
      baudrate = atoi(argv[2]);
  }

  printf("Device: %s, Baudrate: %d\n", device, baudrate);

  fd = open(device, O_RDWR | O_NOCTTY);
  if (fd < 0) {
    perror("Failed to open device");
    return -1;
  }

  if (configure_serial(fd, baudrate) < 0) {
    close(fd);
    return -1;
  }

  printf("Serial port configured successfully\n");

  // --- 如果是扫描模式，执行扫描后退出 ---
  if (scan_mode) {
    scan_bus(fd);
    close(fd);
    return 0;
  }

  printf("Starting to poll devices...\n");
  printf("Press Ctrl+C to exit\n\n");

  // !!! 重要: 使用扫描功能找到真实地址后，请在此处修改 !!!
  uint8_t slave_addresses[] = {0x01, 0x02, 0x03, 0x04};
  int num_slaves = sizeof(slave_addresses) / sizeof(slave_addresses[0]);

  uint8_t command[8];
  uint8_t response[64];

  while (1) {
    for (int i = 0; i < num_slaves; i++) {
      uint8_t current_address = slave_addresses[i];

      command[0] = current_address;
      command[1] = 0x03;
      command[2] = 0x00;
      command[3] = 0x01;
      command[4] = 0x00;
      command[5] = 0x02;
      uint16_t crc = crc16(command, 6);
      command[6] = crc & 0xFF;
      command[7] = (crc >> 8) & 0xFF;

      tcflush(fd, TCIFLUSH);

      write(fd, command, 8);

      printf("Polling Device Address: 0x%02X\n", current_address);
      printf("TX: ");
      for (int k = 0; k < 8; k++) {
        printf("%02X ", command[k]);
      }
      printf("\n");

      usleep(50000); // 延时等待响应

      int bytes_read = read(fd, response, sizeof(response));

      if (bytes_read > 0) {
        printf("RX (%d bytes): ", bytes_read);
        for (int k = 0; k < bytes_read; k++) {
          printf("%02X ", response[k]);
        }
        printf("\n");

        if (bytes_read < 4) {
          printf(" -> Error: Incomplete frame received.\n");
        } else {
          uint16_t received_crc = ((uint16_t)response[bytes_read - 1] << 8) |
                                  response[bytes_read - 2];
          uint16_t calculated_crc = crc16(response, bytes_read - 2);

          if (received_crc != calculated_crc) {
            printf(
                " -> Error: CRC mismatch! Received=0x%04X, Calculated=0x%04X\n",
                received_crc, calculated_crc);
          } else {
            if (response[0] == current_address && response[1] == 0x03 &&
                response[2] == 4) {
              uint16_t low16 = ((uint16_t)response[3] << 8) | response[4];
              uint16_t high16 = ((uint16_t)response[5] << 8) | response[6];
              uint32_t value = ((uint32_t)high16 << 16) | low16;
              printf(" -> OK! 32-bit value: %u (0x%08X)\n", value, value);
            } else {
              printf(
                  " -> Warning: CRC OK, but response format is unexpected.\n");
            }
          }
        }
      } else {
        printf(" -> RX: No response from address 0x%02X (timeout or error)\n",
               current_address);
      }

      printf("\n");
      usleep(100000); // 轮询下一个设备前的间隔
    }
  }

  close(fd);
  return 0;
}
